package com.zzedu.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zzedu.common.enums.CommonEnums;
import com.zzedu.domain.*;
import com.zzedu.enmuns.InteractionTypeEnums;
import com.zzedu.enmuns.SubjectTypeEnums;
import com.zzedu.framework.lock.IDistributedLockService;
import com.zzedu.framework.transaction.ITransactionHandlerService;
import com.zzedu.service.*;
import com.zzedu.vo.StuLearnReportDataVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component("jobService")
public class JobServiceImpl implements IJobService {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    ITransactionHandlerService transactionHandlerService;
    @Autowired
    IDistributedLockService distributedLockService;
    @Autowired
    IZzStudentLearnReportService zzStudentLearnReportService;
    @Autowired
    IZzSubjectContentService zzSubjectContentService;

    @Autowired
    IZzExerciseBookService zzExerciseBookService;

    @Autowired
    IZzCourseService zzCourseService;

    @Autowired
    IZzSchoolService zzSchoolService;

    @Autowired
    IZzSchoolCourseService zzSchoolCourseService;

    @Autowired
    IZzChargeOrderService zzChargeOrderService;

    @Autowired
    IZzStuRenewOrderService zzStuRenewOrderService;

    @Autowired
    IZzStudentService zzStudentService;

    @Override
    public void buildReport() {
        logger.info("开始生成报告");
        try {
            createReportTask();
        } catch (Exception e) {
            logger.error("生成报告失败,详细:{}", e.getMessage());
            return;
        }
        //从课程表中获取所有已经解锁的带review 标记的绘本
        LambdaQueryWrapper<ZzStudentLearnReport> queryWrapper = Wrappers.lambdaQuery(ZzStudentLearnReport.class);
        queryWrapper.eq(ZzStudentLearnReport::getIsFinish, CommonEnums.NO.getCode());
        boolean isRun = true;
        long pageNum = 1L;
        long pageSize = 100L;
        while (isRun) {
            IPage<ZzStudentLearnReport> page = this.zzStudentLearnReportService.page(Page.of(pageNum, pageSize, false), queryWrapper);
            isRun = CollectionUtils.isNotEmpty(page.getRecords());
            if (isRun) {
                for (ZzStudentLearnReport zzStudentLearnReport : page.getRecords()) {
                    try {
                        createReport(zzStudentLearnReport);
                    } catch (Exception e) {
                        logger.error("{},生成报告失败,详细:{}", zzStudentLearnReport.getId(), e.getMessage());
                    }
                }
            }
            pageNum++;
        }
        logger.info("生成报告执行结束");
    }

    public void unLockCourseToSchool() {
        logger.info("解锁学校课程开始");
        LambdaQueryWrapper<ZzSchool> queryWrapper = Wrappers.lambdaQuery(ZzSchool.class);
        queryWrapper.gt(ZzSchool::getSignDateEnd, LocalDate.now());
        queryWrapper.eq(ZzSchool::getState, CommonEnums.YES.getCode());
        boolean isRun = true;
        long pageNum = 1L;
        long pageSize = 100L;
        while (isRun) {
            IPage<ZzSchool> page = this.zzSchoolService.page(Page.of(pageNum, pageSize, false), queryWrapper);
            isRun = CollectionUtils.isNotEmpty(page.getRecords());
            if (isRun) {
                for (ZzSchool zzSchool : page.getRecords()) {
                    try {
                        unLockCourse(zzSchool);
                    } catch (Exception e) {
                        logger.error("{},解锁校区课程失败,详细:{}", zzSchool.getId(), e.getMessage());
                    }
                }
            }
            pageNum++;
        }

        logger.info("解锁学校课程结束");
    }

    @Override
    public void autoBuildChargeOrder() {
        String lockName = "auto:charge:order:lock:create";
        LocalDate now = LocalDate.now();
        distributedLockService.executeWithLock(lockName, 45L, 450L, TimeUnit.SECONDS, () -> {
            List<ZzSchool> zzSchoolList = this.zzSchoolService.list();
            LocalDateTime startOfMonth = null;
            LocalDateTime endOfMonth = now.plusDays(-1L).atTime(LocalTime.of(23, 59, 59));
            for (ZzSchool zzSchool : zzSchoolList) {
                ZzChargeOrder lastZzChargeOrder = zzChargeOrderService.getOne(Wrappers.lambdaQuery(ZzChargeOrder.class).eq(ZzChargeOrder::getSchoolId, zzSchool.getId()).orderByDesc(ZzChargeOrder::getId).last("limit 1"));
                if (null == lastZzChargeOrder) {
                    startOfMonth = zzSchool.getGmtCreate();
                } else {
                    startOfMonth = lastZzChargeOrder.getEndDate();
                }

                Integer feeType = zzSchool.getFeeType();
                BigDecimal feeRatio = zzSchool.getRatio();
                BigDecimal referencePrice = zzSchool.getReferencePrice();
                if (null == referencePrice || null == feeRatio || null == feeType) {
                    continue;
                }
                BigDecimal fee = BigDecimal.ZERO;
                // 1-比例 2-固定金额
                if (1 == feeType) {
                    //1个季度 * 续费季度(memberType)
                    fee = referencePrice.multiply(feeRatio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
                } else if (2 == feeType) {
                    fee = feeRatio;
                }
                if (null != zzStuRenewOrderService.getOne(Wrappers.lambdaUpdate(ZzStuRenewOrder.class)
                        .eq(ZzStuRenewOrder::getSchoolId, zzSchool.getId())
                        .ge(ZzStuRenewOrder::getGmtCreate, startOfMonth)
                        .le(ZzStuRenewOrder::getGmtCreate, endOfMonth).last("limit 1 ")
                )) {
                    ZzChargeOrder zzChargeOrder = new ZzChargeOrder();
                    zzChargeOrder.setId(null);
                    zzChargeOrder.setCalcMonth(endOfMonth.format(DateTimeFormatter.ofPattern("yyyy")) + "Q" + getQuarter(endOfMonth));
                    zzChargeOrder.setSchoolId(zzSchool.getId());
                    zzChargeOrder.setSchoolName(zzSchool.getSchoolName());
                    zzChargeOrder.setLinkMan(zzSchool.getContacts());
                    zzChargeOrder.setFee(fee);
                    zzChargeOrder.setStuCount(zzSchool.getStudentCapacity());
                    zzChargeOrder.setStartDate(startOfMonth);
                    zzChargeOrder.setEndDate(endOfMonth);
                    zzChargeOrder.setPayState(CommonEnums.NO.getCode());
                    zzChargeOrderService.save(zzChargeOrder);
                    zzStuRenewOrderService.update(Wrappers.lambdaUpdate(ZzStuRenewOrder.class)
                            .eq(ZzStuRenewOrder::getSchoolId, zzSchool.getId())
                            .ge(ZzStuRenewOrder::getGmtCreate, startOfMonth)
                            .le(ZzStuRenewOrder::getGmtCreate, endOfMonth)
                            .set(ZzStuRenewOrder::getPayChargeId, zzChargeOrder.getId())

                    );
                }
            }
            return true;
        });
    }

    @Override
    public void autoSettingSchoolState(int state) {
        LambdaUpdateWrapper<ZzSchool> updateWrapper = Wrappers.lambdaUpdate(ZzSchool.class);
        updateWrapper.set(ZzSchool::getState, state);
        updateWrapper.apply("id in(select school_id from zz_charge_order where deleted=0 and pay_state=1)");
        this.zzSchoolService.update(updateWrapper);
        this.zzSchoolService.refreshCacheAll();
    }

    @Override
    public void autoSettingStudentExpire() {
        LocalDateTime now = LocalDateTime.now();
        List<ZzStuRenewOrder> pendingOrders = zzStuRenewOrderService.list(
                Wrappers.lambdaQuery(ZzStuRenewOrder.class)
                        .eq(ZzStuRenewOrder::getValidFlag, 1)
                        .le(ZzStuRenewOrder::getStartDate, now)
                        .orderByAsc(ZzStuRenewOrder::getStuId, ZzStuRenewOrder::getStartDate)
        );
        // 按学生分组，每名学生只取第一个（避免重复激活）
        Map<Long, ZzStuRenewOrder> studentToActivate = new LinkedHashMap<>();
        for (ZzStuRenewOrder order : pendingOrders) {
            studentToActivate.putIfAbsent(order.getStuId(), order);
        }
        if (!studentToActivate.isEmpty()) {
            transactionHandlerService.runInTransaction(() -> {
                for (ZzStuRenewOrder order : studentToActivate.values()) {
                    // 激活订单
                    order.setValidFlag(2);
                    zzStuRenewOrderService.updateById(order);

                    // 更新学生状态
                    ZzStudent student = zzStudentService.getById(order.getStuId());
                    if (student != null) {
                        student.setMemberType(order.getRenewType());
                        student.setExpireAt(order.getExpireDate());
                        zzStudentService.updateById(student);
                    }
                }
                return true;
            });
        }
        // 第二步：过期所有 "已生效" 但 "已超期" 的订单
        zzStuRenewOrderService.update(Wrappers.lambdaUpdate(ZzStuRenewOrder.class)
                .set(ZzStuRenewOrder::getValidFlag, 3) // 3=过期
                .eq(ZzStuRenewOrder::getValidFlag, 2)
                .le(ZzStuRenewOrder::getExpireDate, now)
        );
    }

    private void unLockCourse(ZzSchool zzSchool) {
        String lockName = "unlock:course:school:" + zzSchool.getId();
        distributedLockService.executeWithLock(lockName, 45L, 45L, TimeUnit.SECONDS, () -> {
            List<String> levelList = JSON.parseArray(zzSchool.getLevel(), String.class);
            if (null != levelList) {
                for (String level : levelList) {
                    //判断本月是否已经有解锁记录了
                    if (null != this.zzSchoolCourseService.getOne(Wrappers.lambdaQuery(ZzSchoolCourse.class).eq(ZzSchoolCourse::getLevel, level).eq(ZzSchoolCourse::getSchoolId, zzSchool.getId()).ge(ZzSchoolCourse::getGmtCreate, LocalDateTime.of(LocalDate.now().withDayOfMonth(1), LocalTime.MIN)).last("limit 1"))) {
                        continue;
                    }
                    //获取解锁
                    ZzCourse unLockZzCourse = zzCourseService.getOne(Wrappers.lambdaQuery(ZzCourse.class).eq(ZzCourse::getLevel, level).eq(ZzCourse::getState, CommonEnums.YES.getCode()).notExists("select unit_id from zz_school_course where deleted=0 and school_id={0} and level={1} and unit_id=zz_course.id", zzSchool.getId(), level).orderByAsc(ZzCourse::getSortNo).orderByAsc(ZzCourse::getId).last("limit 1"));
                    if (null != unLockZzCourse) {
                        ZzSchoolCourse zzSchoolCourse = new ZzSchoolCourse();
                        zzSchoolCourse.setLevel(level);
                        zzSchoolCourse.setUnitId(unLockZzCourse.getId());
                        zzSchoolCourse.setUnLockDate(LocalDateTime.of(LocalDate.now().withDayOfMonth(1), LocalTime.MIN));
                        zzSchoolCourse.setSchoolId(zzSchool.getId());
                        if (null == zzSchoolCourseService.getOne(Wrappers.lambdaQuery(ZzSchoolCourse.class).eq(ZzSchoolCourse::getSchoolId, zzSchool.getId()).eq(ZzSchoolCourse::getUnitId, unLockZzCourse.getId()).last("limit 1 "))) {
                            zzSchoolCourseService.save(zzSchoolCourse);
                        }
                    }
                }
            }
            return true;
        });
    }


    private void createReportTask() {
        //T+1 的任务,获取昨天 提交的作业数据,看是否需要生成任务
        //获取昨天的提交的所有 绘本ID
        boolean isRun = true;
        long pageNum = 1L;
        long pageSize = 100L;
        LocalDate localDate = LocalDate.now();
        while (isRun) {
            IPage<Map<String, Object>> page = zzSubjectContentService.getSubmitDataByDate(Page.of(pageNum, pageSize, false), LocalDateTime.of(localDate.plusDays(-1), LocalTime.MIN), LocalDateTime.of(localDate.plusDays(-1), LocalTime.MAX));
            isRun = CollectionUtils.isNotEmpty(page.getRecords());
            if (isRun) {
                for (Map map : page.getRecords()) {
                    Long bookId = MapUtils.getLong(map, "picture_book_id");
                    Long stuId = MapUtils.getLong(map, "student_id");
                    Long unitId = MapUtils.getLong(map, "unit_id");
                    if (null == zzStudentLearnReportService.getOne(Wrappers.lambdaQuery(ZzStudentLearnReport.class).eq(ZzStudentLearnReport::getUnitId, unitId).eq(ZzStudentLearnReport::getStuId, stuId).last(" limit 1 "))) {
                        //生成报告
                        ZzStudentLearnReport zzStudentLearnReport = new ZzStudentLearnReport();
                        zzStudentLearnReport.setUnitId(unitId);
                        zzStudentLearnReport.setStuId(stuId);
                        zzStudentLearnReport.setBookId(bookId);
                        zzStudentLearnReport.setIsFinish(CommonEnums.NO.getCode());
                        if (zzStudentLearnReportService.isFinish(bookId, stuId)) {
                            zzStudentLearnReportService.save(zzStudentLearnReport);
                        }
                    }
                }
            }
            pageNum++;
        }
    }

    /**
     * 生成报告
     *
     * @param zzStudentLearnReport
     */
    private void createReport(ZzStudentLearnReport zzStudentLearnReport) {
        String lockName = "report:" + zzStudentLearnReport.getId();
        distributedLockService.executeWithLock(lockName, 1L, 60L, TimeUnit.SECONDS, () -> transactionHandlerService.runInTransaction(() -> {
            //根据unit 进行数据统计汇总
            Long submitCountAudio = zzExerciseBookService.getSubmitCount(zzStudentLearnReport.getUnitId(), zzStudentLearnReport.getStuId(), InteractionTypeEnums.dub.getCode());
            if (null == submitCountAudio) {
                submitCountAudio = 0L;
            }
            //全部提交次数
            Long submitCount = zzExerciseBookService.getSubmitCount(zzStudentLearnReport.getUnitId(), zzStudentLearnReport.getStuId(), null);
            if (null == submitCount) {
                submitCount = 0L;
            }
            //视频时长
            Long videoTimes = zzExerciseBookService.getVideoTimes(zzStudentLearnReport.getUnitId(), zzStudentLearnReport.getStuId(), SubjectTypeEnums.video.getCode());
            if (null == videoTimes) {
                videoTimes = 0L;
            }
            StuLearnReportDataVO stuLearnReportDataVO = new StuLearnReportDataVO();
            stuLearnReportDataVO.setSubmitCount(submitCount);
            stuLearnReportDataVO.setVideoTimeS(videoTimes);
            stuLearnReportDataVO.setVideoTime(new BigDecimal(String.valueOf(videoTimes)).divide(new BigDecimal("60"), 1, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString());
            stuLearnReportDataVO.setSubmitCountAudio(submitCountAudio);
            zzCourseService.getWordsAndSentences(stuLearnReportDataVO, zzStudentLearnReport.getUnitId());
            stuLearnReportDataVO.setWordCount(stuLearnReportDataVO.getWords().size());
            stuLearnReportDataVO.setSentenceCount(stuLearnReportDataVO.getSentences().size());

            LambdaUpdateWrapper<ZzStudentLearnReport> updateWrapper = Wrappers.lambdaUpdate(ZzStudentLearnReport.class);
            updateWrapper.set(ZzStudentLearnReport::getContent, JSON.toJSONString(stuLearnReportDataVO));
            updateWrapper.set(ZzStudentLearnReport::getIsFinish, CommonEnums.YES.getCode());
            updateWrapper.set(ZzStudentLearnReport::getReadFlag, CommonEnums.NO.getCode());
            updateWrapper.set(ZzStudentLearnReport::getReadCount, 0);
            updateWrapper.eq(ZzStudentLearnReport::getId, zzStudentLearnReport.getId());
            updateWrapper.eq(ZzStudentLearnReport::getIsFinish, CommonEnums.NO.getCode());
            zzStudentLearnReportService.update(updateWrapper);
            return true;
        }));

    }

    /**
     * 获取当前结束日期季度
     *
     * @param dateTime
     * @return
     */
    private int getQuarter(LocalDateTime dateTime) {
        if (dateTime == null) {
            throw new IllegalArgumentException("日期时间不能为 null");
        }
        int month = dateTime.getMonthValue(); // 1-12
        return (month - 1) / 3 + 1; // 简洁公式：1~3月=1，4~6月=2，7~9月=3，10~12月=4
    }
}
